#include<stdio.h>
#include<iostream>
#include<pthread.h>
#include<queue>

#define MAX_QUEUE 5

//生产者与消费者模型---------------阻塞队列
//封装一个阻塞队列
template<class T>
class BlockQueue{
  private:
    std::queue<T> _q;  //声明一个队列
    int _capacity;
    pthread_mutex_t _mutex; //互斥
    pthread_cond_t _cond_pro;//生产者条件变量
    pthread_cond_t _cond_con;//消费者条件变量
public:
    BlockQueue(int capacity=MAX_QUEUE):_capacity(capacity){
      pthread_mutex_init(&_mutex,NULL); //初始化
      pthread_cond_init(&_cond_pro,NULL);
      pthread_cond_init(&_cond_con,NULL);
    }
    ~BlockQueue()
    {
      pthread_mutex_destroy(&_mutex);
      pthread_cond_destroy(&_cond_con);
      pthread_cond_destroy(&_cond_pro);
    }
    bool Push(const T& data){  //const 引用表示参数是输入参数
      //入队操作-----生产者
      //一旦队列容量达到上线则阻塞
      while(_capacity==_q.size())
      {
        //容量满了,阻塞生产者
        pthread_cond_wait(&_cond_pro,&_mutex);
      }
      //容量没满
      _q.push(data);
      pthread_cond_signal(&_cond_con);  //唤醒一个或多个消费者
      pthread_mutex_unlock(&_mutex);     //解锁
    }
    bool Pop(T* data)        //指针类型表示参数是输出参数
    {
      //出队操作-----消费者
      //有数据出队，没有数据则阻塞消费者
      pthread_mutex_lock(&_mutex);
      while(_q.empty()){
        //空队列--阻塞消费者
        pthread_cond_wait(&_cond_con,&_mutex);
      }
      //队列不空
      *data=_q.front();
      _q.pop();
      pthread_cond_signal(&_cond_pro); //唤醒生产者
      pthread_mutex_unlock(&_mutex);
    }
};

#define MAX_USER 4

//队列先进先出，会使得结果显式push多个，pop多个
void* consumer(void* arg)
{
  BlockQueue<int> *q=(BlockQueue<int>*)arg;
  while(1){
    //消费者---出队
    int data; 
    q->Pop(&data);   //并非原子性操作----可能出队之后还没来得及打印就已经切换了进程，导致多次打印
    printf("------%p thread get data:%d\n",pthread_self(),data);
  }
  return NULL;
}

void* productor(void* arg)
{
  BlockQueue<int> *q=(BlockQueue<int>*)arg;
  int data=0;//局部变量，不同线程各入各的数据
  while(1){
    //生产者---入队
    q->Push(data);
    printf("++++++%p thread put data:%d\n",pthread_self(),data++);
  }

  return NULL;
}

int main()
{
  BlockQueue<int> q;   //定义一个队列
  //创建多个线程
  pthread_t con_tid[MAX_USER],pro_tid[MAX_USER];
  int ret;
  for(int i=0;i<MAX_USER;++i){
    ret=pthread_create(&con_tid[i],NULL,consumer,(void*)&q);
    if(ret!=0){
      std::cout<<"pthread_create error!\n";
      return -1;
    }
    ret=pthread_create(&pro_tid[i],NULL,productor,(void*)&q);
     if(ret!=0){
       std::cout<<"pthread_create error!\n";
       return -1;
     }
  }


  //线程等待
  for(int i=0;i<MAX_USER;++i){
    pthread_join(con_tid[i],NULL);
    pthread_join(pro_tid[i],NULL);
  }

  return 0;
}

